#include <IDK.hpp>

#include <OES.hpp>
#include <RenderStateOES.hpp>

namespace Renderer
{

	struct PixelInfo
	{
		GLenum format;
		GLenum type;
	};

	static const PixelInfo PixelTypeInfo[IDK::Pixel::Type::MAX] = 
	{
		{ GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 },
		{ GL_RGBA, GL_RGBA },
		{ 0/*GL_COMPRESSED_RGB_S3TC_DXT1_EXT*/, GL_RGB,},
		{ 0/*GL_COMPRESSED_RGBA_S3TC_DXT1_EXT*/, GL_RGBA, },
		{ 0/*GL_COMPRESSED_RGBA_S3TC_DXT3_EXT*/, GL_RGBA, },
		{ 0/*GL_COMPRESSED_RGBA_S3TC_DXT5_EXT*/, GL_RGBA, },
		{ GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, GL_RGBA },
		{ GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, GL_RGBA },
	};

	template<typename TInterface>
	class TOESHandle : public THandle, virtual public TInterface
	{
		IDKClass;
	protected:
		GLuint mHandle;

	public:
		TOESHandle() : mHandle(Invalid) 
		{
		}
	private:
		virtual bool IsInvalid() //
		{
			return mHandle == Invalid;
		}

		virtual void Dispose()
		{
			if(mHandle != Invalid)
			{
				Finalize();
				mHandle = Invalid;
			}
		}
	protected:
		virtual void Finalize() = 0;
	};


	//
	//
	//
	class TOESIndexBuffer : public TOESHandle<IDK::IGPUIndexBuffer>
	{
	public:
		TOESIndexBuffer(uint indexSize, uint count, HandlePtr data)
		{
			glValid(glGenBuffers(1, &mHandle));
			glValid(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mHandle)); 

			UnSafeConstPtr pData = data->GetPointer();
			glValid(glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize * count, pData, GL_STATIC_DRAW));
		}

		virtual void Bind()
		{
			glValid(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mHandle)); 
		}

		virtual void Unbind()
		{
			glValid(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); 
		}

		virtual void Finalize()
		{
			glValid(glDeleteBuffers(1, &mHandle));
		}
	};

	//
	//
	//
	class TOESVertexBuffer : public TOESHandle<IDK::IGPUVertexBuffer>
	{
		uint mVertexSize;
	public:
		TOESVertexBuffer(uint vertexSize, uint count, HandlePtr data)
			: mVertexSize(vertexSize)
		{
			glValid(glGenBuffers(1, &mHandle));
			glValid(glBindBuffer(GL_ARRAY_BUFFER, mHandle));

			UnSafeConstPtr pData = data->GetPointer();
			glValid(glBufferData(GL_ARRAY_BUFFER, vertexSize * count, pData, GL_STATIC_DRAW));
		}

		virtual void Bind(const IDK::GPUVertexBinding& binding)
		{
			glValid(glBindBuffer(GL_ARRAY_BUFFER, mHandle));

			for(int i = 0; i!=IDK::GPUVertexBinding::Type::MAX; ++i)
			{
				if(binding.bind[i] != Invalid)
				{
					//apply attrib pointer
					glValid(glVertexAttribPointer(binding.bind[i],
						3,
						GL_FLOAT,
						GL_FALSE,
						mVertexSize,
						(UnSafePtr)((i - IDK::GPUVertexBinding::Type::Position) * sizeof(Math::Vector3)))); 

					glValid(glEnableVertexAttribArray(binding.bind[i]));	
				}
			}
		}

		virtual void Unbind(const IDK::GPUVertexBinding& binding)
		{
			for(int i = 0; i!=IDK::GPUVertexBinding::Type::MAX; ++i)
			{
				if(binding.bind[i] != Invalid)
				{
					glValid(glDisableVertexAttribArray(binding.bind[i]));	
				}
			}

			glValid(glBindBuffer(GL_ARRAY_BUFFER, 0));
		}

		virtual void Finalize()
		{
			glValid(glDeleteBuffers(1, &mHandle));
		}
	};
	//
	//
	//
	template<typename TInterface = IDK::IGPUTexture>
	class TOESTexture : public TOESHandle<TInterface>
	{
	private:
		IDK::Pixel::Filter::Enum mFilter;
		IDK::Pixel::Type::Enum mPixelType;
	public:
		TOESTexture(IDK::Pixel::Type::Enum pixelType, IDK::Pixel::Filter::Enum filter) 
			: mPixelType(pixelType),
			mFilter(filter)
		{
			glValid(glGenTextures(1, &mHandle));

			glValid(glBindTexture(GL_TEXTURE_2D, mHandle));

			ApplyFilter();

			glValid(glBindTexture(GL_TEXTURE_2D, 0));
		}

		void ApplyFilter()
		{
			switch(mFilter)
			{
			case IDK::Pixel::Filter::None:
				glValid(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
				glValid(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
				break;

			case IDK::Pixel::Filter::Point:
				glValid(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
				glValid(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
				break;

			case IDK::Pixel::Filter::MipMap:
				glValid(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
				glValid(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
				break;
			}
		}

		virtual void Update(uint level, uint w, uint h, HandlePtr data, uint size )
		{
			const PixelInfo& pixelInfo = PixelTypeInfo[mPixelType];

			switch(pixelInfo.format)
			{
			case GL_RGBA : //uncompress
				glValid(glTexImage2D(GL_TEXTURE_2D, level, pixelInfo.format, w, h, 0, pixelInfo.format, pixelInfo.type, data->GetPointer()));
				break;
			default:
				glValid(glCompressedTexImage2D(GL_TEXTURE_2D, level, pixelInfo.format, w, h, 0, size, data->GetPointer()));
				break;
			}
			//
			//glValid(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, l));
		}

		virtual void Bind(uint index)
		{
			glValid(glActiveTexture(GL_TEXTURE0 + index));
			glValid(glBindTexture(GL_TEXTURE_2D, mHandle));
		}

		virtual void Unbind(uint index)
		{
			glValid(glActiveTexture(GL_TEXTURE0 + index));
			glValid(glBindTexture(GL_TEXTURE_2D, 0));
		}

		virtual void Finalize()
		{
			glValid(glDeleteTextures(1, &mHandle));
		}
	};

	//
	//
	class TOESRenderTexture : public TOESTexture<IDK::IGPURenderTexture>
	{
		IDKClass;
	private:
	public:
		TOESRenderTexture(uint width, uint height, 
			IDK::Pixel::Type::Enum pixelType,
			IDK::Pixel::Filter::Enum filter) 
			: TOESTexture(pixelType, filter)
		{
			glValid(glBindTexture(GL_TEXTURE_2D, mHandle));

			const PixelInfo& pixelInfo = PixelTypeInfo[pixelType];

			glValid(glTexImage2D(GL_TEXTURE_2D, 0, pixelInfo.format, width, height, 0, pixelInfo.type, GL_UNSIGNED_BYTE, 0));

			glBindTexture(GL_TEXTURE_2D, 0);
		}

		virtual void Attach(uint index)
		{
			glValid(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, GL_TEXTURE_2D, mHandle, 0));
		}
	};

	typedef ref<TOESRenderTexture> OESRenderTexture;

	class TOESRenderTarget : public TOESHandle<IDK::IGPURenderTarget>
	{
	private:
		GLuint mDepthHandle;
		uint mWidth;
		uint mHeight;
	public:
		TOESRenderTarget(uint width, uint height) : mWidth(width), mHeight(height)
		{
			glValid(glGenFramebuffers(1, &mHandle));
			glValid(glGenRenderbuffers(1, &mDepthHandle));

			glValid(glBindFramebuffer(GL_FRAMEBUFFER, mHandle));
			glValid(glBindRenderbuffer(GL_RENDERBUFFER, mDepthHandle));

			glValid(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height));
			glValid(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepthHandle));

			const char* FrameBufferErrorMessage[] =
			{
				"Framebuffer Complete",
				"Incomplete attachment",
				"Missing attachment",
				"Duplicate attachment",
				"Incomplete dimensions",
				"Incomplete formats",
				"Incomplete draw buffer",
				"Incomplete read buffer",
				"Framebuffer unsupported"
			};

			GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
			if(status != GL_FRAMEBUFFER_COMPLETE)
			{
				iLog("Frame buffer error:");
				iLog(FrameBufferErrorMessage[status-GL_FRAMEBUFFER_COMPLETE]);
			}
			else
			{
				iLog("Frame buffer create: OK");
			}
		}

		virtual void Bind()
		{
			glValid(glBindFramebuffer(GL_FRAMEBUFFER, mHandle));
			glValid(glViewport(0, 0, mWidth, mHeight));
		}

		virtual void Unbind()
		{
			glValid(glBindFramebuffer(GL_FRAMEBUFFER, 0));
			glValid(glViewport(0, 0, DefualtDeviceConfig::ScreenWidth, DefualtDeviceConfig::ScreenHeight));
		}

		virtual void Finalize()
		{
			glValid(glDeleteRenderbuffers(1, &mDepthHandle)); 
			glValid(glDeleteFramebuffers(1, &mHandle));
		}
	};

	typedef ref<TOESRenderTarget> OESRenderTarget;

	class TOESShader : public TOESHandle<IDK::IGPUShader>
	{
	private:
		bool CheckShader(GLuint handle, GLenum param)
		{
			GLint glslOk = 0;

			glGetShaderiv(handle, param, &glslOk);

			if(glslOk == GL_FALSE)
			{
				int ilen = 0;

				glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &ilen);

				if (ilen > 0)
				{
					{
						iLog("Shader compile log:");
						TDynamicArray<char> info(ilen);
						glGetShaderSource(handle, ilen, 0, info.GetMemory());
						iLog(info.GetMemory());
					}

					{
						TDynamicArray<char> info(ilen);
						glGetShaderInfoLog(handle, ilen, &glslOk, info.GetMemory());

						iLog(info.GetMemory());
						iLog("shader error...fail");
					}
					return false;
				}
			}
			return true;
		}

		bool CheckProgram(GLuint handle, GLenum param)
		{
			GLint glslOk = 0;

			glGetProgramiv(handle, param, &glslOk);
			if(glslOk == GL_FALSE)
			{
				int ilen = 0;

				glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &ilen);

				if (ilen > 0)
				{
					{
						TDynamicArray<char> info(ilen);
						glGetProgramInfoLog(handle, ilen, &glslOk, info.GetMemory());

						iLog(info.GetMemory());
						iLog("shader error...fail");
					}
					return false;
				}
			}
			return true;
		}
	public:
		TOESShader(IDK::Stream vertexStream, IDK::Stream fragmentStream, IDK::ShaderParameters parameters)
		{
			//Gen Vertex Shader
			GLuint vertID = glCreateShader(GL_VERTEX_SHADER);
			{
				vertexStream->Open();
				String vertSrc = vertexStream->ReadText();
				vertexStream->Close();

				int len = vertSrc->Length();
				const char* text = vertSrc->Chars();
				iLog("Gen vertex shader: BEGIN");
				glShaderSource(vertID, 1, &text, &len);
				glCompileShader(vertID);

				if(CheckShader(vertID, GL_COMPILE_STATUS))
				{
					iLog("Gen vertex shader: OK");
				}
				else
				{
					iLog(text);
					iError("Gen vertex shader: Fail");
				}
			}

			//Gen Fragment Shader
			GLuint fragID = glCreateShader(GL_FRAGMENT_SHADER);
			{
				fragmentStream->Open();
				String fragSrc = fragmentStream->ReadText();
				fragmentStream->Close();

				int len = fragSrc->Length();
				const char* text = fragSrc->Chars();
				
				iLog("Gen frag shader: BEGIN");

				glShaderSource(fragID, 1, &text, &len);
				glCompileShader(fragID);

				if(CheckShader(fragID, GL_COMPILE_STATUS))
				{
					iLog("Gen frag shader: OK");
				}
				else
				{
					iLog(text);
					iError("Gen frag shader: Fail");
				}
			}
			//
			mHandle = glCreateProgram();

			glAttachShader(mHandle, vertID);
			glAttachShader(mHandle, fragID);

			glLinkProgram(mHandle);

			CheckProgram(mHandle, GL_LINK_STATUS);
			glValidateProgram(mHandle);
			CheckProgram(mHandle, GL_VALIDATE_STATUS); 

			glDeleteShader(vertID);
			glDeleteShader(fragID);

			//Bind Location
			for(IDK::ShaderParameters::Iterator it = parameters->Begin(), end = parameters->End();
				it!=end;
				++it)
			{
				uint id = Invalid;
				const char* name = it->GetName()->Chars();

				switch(it->GetType())
				{
				case IDK::IShaderParameter::Type::VertexAttribute :
					it->SetID(id = glGetAttribLocation(mHandle, name));
					break;

				case IDK::IShaderParameter::Type::ConstantParameter :
					it->SetID(id = glGetUniformLocation(mHandle, name));
					break;
				}

				if(id == Invalid)
				{
					iLog("WARNING!!! Invalid parameter location:");
					iLog(name);
				}
			}
		}

		virtual void Bind()
		{
			glUseProgram(mHandle);
		}

		virtual void Unbind()
		{
			glUseProgram(0);
		}

		virtual void Finalize()
		{
			glDeleteProgram(mHandle);
		}
	};

	IDK::GPURenderTexture TRenderStateOES::CreateRenderTexture(uint width, uint height,
		IDK::Pixel::Type::Enum pixelType,
		IDK::Pixel::Filter::Enum filter)
	{
		IDK::GPURenderTexture res = new TOESRenderTexture(width, height, pixelType, filter);

		mResources->Add(res.As<IDK::IGPUResource>());

		return res;
	}

	IDK::GPUTexture TRenderStateOES::CreateTexture(IDK::Pixel::Type::Enum type, IDK::Pixel::Filter::Enum filter)
	{
		IDK::GPUTexture res = new TOESTexture<>(type, filter);

		mResources->Add(res.As<IDK::IGPUResource>());

		return res;
	}

	void TRenderStateOES::SetTexture(IDK::GPUTexture texture, uint index)
	{
		if(texture == NullPtr) //to disable
		{
			if(mBindTextures[index] != NullPtr)
			{
				mBindTextures[index]->Unbind(index);
			}

			mBindTextures[index] = NullPtr;
		}
		else
		{
			texture->Bind(index);

			mBindTextures[index] = texture;
		}
	}

	void TRenderStateOES::SetRenderTexture(IDK::GPURenderTexture texture, uint index)
	{
		if(texture == NullPtr) //to disable
		{
			//texture->Unattach(index);?
		}
		else
		{
			texture->Attach(index);
		}
	}


	IDK::GPUShader TRenderStateOES::CreateShader(IDK::Stream vertStream, IDK::Stream fragStream, IDK::ShaderParameters parameters)
	{
		IDK::GPUShader res = new TOESShader(vertStream, fragStream, parameters);

		mResources->Add(res.As<IDK::IGPUResource>());

		return res;
	}

	void TRenderStateOES::SetShader(IDK::GPUShader shader)
	{
		if(shader == NullPtr) //to disable
		{
			if(mBindShader != NullPtr)
			{
				mBindShader->Unbind();
			}

			mBindShader = NullPtr;
		}
		else
		{
			shader->Bind();

			mBindShader = shader;
		}
	}

	IDK::GPURenderTarget TRenderStateOES::CreateRenderTarget(uint width, uint heigth)
	{
		IDK::GPURenderTarget res = new TOESRenderTarget(width, heigth);

		mResources->Add(res.As<IDK::IGPUResource>());

		return res;
	}

	void TRenderStateOES::SetRenderTarget(IDK::GPURenderTarget rtt)
	{
		if(rtt == NullPtr) //to disable
		{
			if(mBindRenderTarget != NullPtr)
			{
				mBindRenderTarget->Unbind();
			}

			mBindRenderTarget = NullPtr;
		}
		else
		{
			rtt->Bind();

			mBindRenderTarget = rtt;
		}
	}

	void TRenderStateOES::SetShaderParameters(IDK::ShaderParameters parameters)
	{
		//reset vertex bindings
		for(int i = 0; i!=IDK::GPUVertexBinding::Type::MAX; ++i)
		{
			mVertexBinding.bind[i] = Invalid;
		}

		for(IDK::ShaderParameters::Iterator it = parameters->Begin(), end = parameters->End();
			it!=end; ++it)
		{
			if(it->GetID() == -1)
			{
				continue;
			}
			switch(it->GetType()) //
			{
			case IDK::IShaderParameter::Type::VertexAttribute :
				{
					IDK::IShaderParameter::Declaration::Enum decl = it->GetDeclaration();
					switch(decl)
					{
					case IDK::IShaderParameter::Declaration::VertexPosition:
					case IDK::IShaderParameter::Declaration::VertexNormal:
					case IDK::IShaderParameter::Declaration::VertexTangent:
					case IDK::IShaderParameter::Declaration::VertexTexUV0:
						mVertexBinding.bind[decl] = it->GetID();
						break;
					}
				}

			case IDK::IShaderParameter::Type::ConstantParameter :
				switch(it->GetDeclaration())
				{

				case IDK::IShaderParameter::Declaration::MatrixViewInverse :
					{
						Math::Matrix44 temp = Math::Inverse(mMatrices[MatrixType::View]);
						glUniformMatrix4fv(it->GetID(), 1, GL_FALSE,  Math::ToPtr(temp));	
					}
					break;

				case IDK::IShaderParameter::Declaration::MatrixProjection :
					glUniformMatrix4fv(it->GetID(), 1, GL_FALSE, Math::ToPtr(mMatrices[MatrixType::Projection]));
					break;
				case IDK::IShaderParameter::Declaration::MatrixView :
					glUniformMatrix4fv(it->GetID(), 1, GL_FALSE, Math::ToPtr(mMatrices[MatrixType::View]));	
					break;

				case IDK::IShaderParameter::Declaration::MatrixWorld :
					glUniformMatrix4fv(it->GetID(), 1, GL_FALSE, Math::ToPtr(mMatrices[MatrixType::World]));
					break;

				case IDK::IShaderParameter::Declaration::Constant2f:
					{
						glUniform2fv(it->GetID(), 1, (const GLfloat*)it->GetCustomData()->GetPointer());
					}
					break;

				case IDK::IShaderParameter::Declaration::Constant3f:
					glUniform3fv(it->GetID(), 1, (const GLfloat*)it->GetCustomData()->GetPointer());
					break;

				case IDK::IShaderParameter::Declaration::Constant4f:
					glUniform4fv(it->GetID(), 1, (const GLfloat*)it->GetCustomData()->GetPointer());
					break;

				case IDK::IShaderParameter::Declaration::Constant44f:
					glUniformMatrix4fv(it->GetID(), 1, GL_FALSE, (const GLfloat*)it->GetCustomData()->GetPointer());
					break;

				case IDK::IShaderParameter::Declaration::Texture0:
				case IDK::IShaderParameter::Declaration::Texture1:
				case IDK::IShaderParameter::Declaration::Texture2:
				case IDK::IShaderParameter::Declaration::Texture3:
				case IDK::IShaderParameter::Declaration::Texture4:
					glUniform1i(it->GetID(), it->GetDeclaration() - IDK::IShaderParameter::Declaration::Texture0);
					break;

				case IDK::IShaderParameter::Declaration::TimeFrame:
					glUniform1f(it->GetID(), mTimeFrame);
					break;

				case IDK::IShaderParameter::Declaration::TimeElapsed:
					glUniform1f(it->GetID(), mTimeElapsed);
					break;
				}
				break;
			}
		}
	}

	IDK::GPUIndexBuffer TRenderStateOES::CreateIndexBuffer(uint indexSize, uint count, HandlePtr data)
	{
		IDK::GPUIndexBuffer res = new TOESIndexBuffer(indexSize, count, data);

		mResources->Add(res.As<IDK::IGPUResource>());

		return res;
	}

	IDK::GPUVertexBuffer TRenderStateOES::CreateVertexBuffer(uint vertexSize, uint count, HandlePtr data)
	{
		IDK::GPUVertexBuffer res = new TOESVertexBuffer(vertexSize, count, data);

		mResources->Add(res.As<IDK::IGPUResource>());

		return res;
	}

	void TRenderStateOES::SetIndexBuffer(IDK::GPUIndexBuffer buffer)
	{
		if(buffer == NullPtr) //to disable
		{
			if(mBindIndexBuffer != NullPtr)
			{
				mBindIndexBuffer->Unbind();
			}

			mBindIndexBuffer = NullPtr;
		}
		else
		{
			buffer->Bind();

			mBindIndexBuffer = buffer;
		}
	}

	void TRenderStateOES::SetVertexBuffer(IDK::GPUVertexBuffer buffer)
	{
		if(buffer == NullPtr) //to disable
		{
			if(mBindVertexBuffer != NullPtr)
			{
				mBindVertexBuffer->Unbind(mVertexBinding);
			}

			mBindVertexBuffer = NullPtr;
		}
		else
		{
			buffer->Bind(mVertexBinding);

			mBindVertexBuffer = buffer;
		}
	}


	void TRenderStateOES::DrawIndexVertexData(PrimitiveType::Enum type, uint polyCount)
	{
		static const GLenum PrimitiveTypeToOgl[IDK::IRenderState::PrimitiveType::MAX] = 
		{ 
			GL_LINES,
			GL_TRIANGLES
		};

		switch(type)
		{
		case PrimitiveType::Lines:
			break;
		case PrimitiveType::TrinagleList:
			break;
		}

		glValid(glDrawElements(PrimitiveTypeToOgl[type], polyCount*3, GL_UNSIGNED_SHORT, 0));
	}

	//simple
	//-------------------------------------------------------
	// SetCullFace
	//-------------------------------------------------------
	void TRenderStateOES::SetCullFace(CullFace::Enum mode)
	{
		if(mCullFace == mode)
			return;

		switch(mCullFace = mode)
		{
		case CullFace::None:
			glDisable(GL_CULL_FACE);
			break;

		case CullFace::Front:
			glEnable(GL_CULL_FACE);
			glCullFace(GL_FRONT);
			break;

		case CullFace::Back:
			glEnable(GL_CULL_FACE);
			glCullFace(GL_BACK);
			break;
		}
	}

	void TRenderStateOES::SetBlend(Blend::Enum blend)
	{
		if(mBlend == blend)
			return;

		switch(mBlend = blend)
		{
		case Blend::None:
			glValid(glDisable(GL_BLEND));
			break;

		case Blend::AlphaBlend:
			glValid(glEnable(GL_BLEND));
			glValid(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
			break;
		}
	}

	//-------------------------------------------------------
	// DrawLine
	//-------------------------------------------------------
	void TRenderStateOES::DrawLine(const Math::Vector3& start,const Math::Vector3& end)
	{
		const GLfloat vertices[] = 
		{
			start.x, start.y, start.z,
			end.x, end.y, end.z,
		};

		const GLubyte indices[] = 
		{
			0, 1
		};

		glValid(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float)*3, vertices));
		glValid(glEnableVertexAttribArray(0));

		glValid(glDrawElements(GL_LINES, 2, GL_UNSIGNED_BYTE, indices));

		// Unbind the VBO
		glValid(glBindBuffer(GL_ARRAY_BUFFER, 0));
	}

	//-------------------------------------------------------
	// DrawRect
	//-------------------------------------------------------
	inline float toRanged2D(float x)
	{
		return (x * 2.0f) - 1.0f;
	}

	inline float toInvert(float x)
	{
		return 1.0f - x;
	}
	void TRenderStateOES::DrawRect(const Math::Rect& rect, const Math::Rect& trect, float z)
	{
		//conver to space [-1 .. 1]
		Math::Vector2 bl(toRanged2D(rect.left), toRanged2D(toInvert(rect.bottom)));
		Math::Vector2 tl(toRanged2D(rect.left), toRanged2D(toInvert(rect.top)));

		Math::Vector2 tr(toRanged2D(rect.right), toRanged2D(toInvert(rect.top)));
		Math::Vector2 br(toRanged2D(rect.right), toRanged2D(toInvert(rect.bottom)));
		/*
		Math::Vector2 bl(-1.0, -1.0);
		Math::Vector2 tl(-1.0, 1.0);
		Math::Vector2 tr(1.0, 1.0);
		Math::Vector2 br(1.0, -1.0);*/

		const GLfloat vertices[] = 
		{
			bl.x, bl.y, trect.left, toInvert(trect.bottom), //bottom left corner
			tl.x, tl.y, trect.left, toInvert(trect.top), //top left corner
			tr.x, tr.y, trect.right, toInvert(trect.top), //top right corner
			br.x, br.y, trect.right, toInvert(trect.bottom)  //bottom right corner
		}; 
		/*
		const GLfloat vertices[] = 
		{
		bl.x, bl.y, 0.0f, 0.0f, //bottom left corner
		tl.x, tl.y, 0.0f, 1.0f, //top left corner
		tr.x, tr.y, 1.0, 1.0f, //top right corner
		br.x, br.y, 1.0, 0.0  //bottom right corner
		}; */

		const GLubyte indices[] = 
		{
			0, 1, 2, // first triangle (bottom left - top left - top right)
			0, 2, 3
		}; // second triangle (bottom left - top right - bottom right)


		glValid(glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(float)*4, vertices));
		glValid(glEnableVertexAttribArray(0));

		glValid(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices));

		// Unbind the VBO
		glValid(glBindBuffer(GL_ARRAY_BUFFER, 0));
	};

	void TRenderStateOES::ClearBuffers()
	{
		glValid(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
		glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
	}

	void TRenderStateOES::SetDepthTest(DepthTest::Enum mode)
	{
		if(mDepthTest == mode)
			return;

		switch(mDepthTest = mode)
		{
		case DepthTest::None:
			glDisable(GL_DEPTH_TEST);
			break;

		case DepthTest::LessEqual:
			glEnable(GL_DEPTH_TEST);
			glDepthFunc(GL_LEQUAL);
			break;	
		}
	}	
}